home *** CD-ROM | disk | FTP | other *** search
/ NeXT Education Software Sampler 1992 Fall / NeXT Education Software Sampler 1992 Fall.iso / Programming / Source / winterp-1.13 / examples / mail-br.lsp < prev    next >
Encoding:
Lisp/Scheme  |  1991-10-06  |  11.2 KB  |  329 lines

  1. ; -*-Lisp-*-
  2. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  3. ;
  4. ; File:         mail-browser.lsp
  5. ; RCS:          $Header: mail-br.lsp,v 1.3 91/10/05 18:24:09 mayer Exp $
  6. ; Description:  A simple MH mail browser written mostly to show the power of
  7. ;               subclassing the Motif list widget in WINTERP. Load this file
  8. ;               to get a browser of the last 30 MH messages in your inbox.
  9. ;               This assumes that (1) you have MH, (2) you have folder +inbox,
  10. ;               (3) "scan" is on your $PATH. (4) various other things I forgot.
  11. ; Author:       Niels Mayer, HPLabs
  12. ; Created:      Mon Nov 20 18:13:23 1989
  13. ; Modified:     Sat Oct  5 18:23:48 1991 (Niels Mayer) mayer@hplnpm
  14. ; Language:     Lisp
  15. ; Package:      N/A
  16. ; Status:       X11r5 contrib tape release
  17. ;
  18. ; WINTERP Copyright 1989, 1990, 1991 Hewlett-Packard Company (by Niels Mayer).
  19. ; XLISP version 2.1, Copyright (c) 1989, by David Betz.
  20. ;
  21. ; Permission to use, copy, modify, distribute, and sell this software and its
  22. ; documentation for any purpose is hereby granted without fee, provided that
  23. ; the above copyright notice appear in all copies and that both that
  24. ; copyright notice and this permission notice appear in supporting
  25. ; documentation, and that the name of Hewlett-Packard and Niels Mayer not be
  26. ; used in advertising or publicity pertaining to distribution of the software
  27. ; without specific, written prior permission.  Hewlett-Packard and Niels Mayer
  28. ; makes no representations about the suitability of this software for any
  29. ; purpose.  It is provided "as is" without express or implied warranty.
  30. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  31.  
  32. ;;
  33. ;; Make a subclass of XM_LIST_WIDGET_CLASS which holds an additional
  34. ;; instance variable 'items'. 'items' is an array of arbitrary objects
  35. ;; (BROWSER_OBJECT) to be displayed in a browser made from the list widget.
  36. ;;
  37. ;; BROWSER-OBJECT can be any arbitrary xlisp object that respond to
  38. ;; the messages :display_string and :default_action:
  39. ;;
  40. ;; Message :display_string must return a string which is used as the
  41. ;; textual representation of the object in the browser display.
  42. ;;
  43. ;; Message :default_action is sent to the object whenever the
  44. ;; list widget's default action, a double-click, is performed on the item
  45. ;; corresponding to the object.
  46. ;; 
  47. (setq List_Browser 
  48.       (send Class :new
  49.         '(items)            ;new instance vars
  50.         '()                ;no class vars
  51.         XM_LIST_WIDGET_CLASS))    ;superclass
  52.  
  53. ;;
  54. ;; We override the XM_LIST_WIDGET_CLASS's object initializer
  55. ;; so that we can process the items list and hand off the
  56. ;; browser items to the list widget.
  57. ;;
  58. ;; (send List_Browser :new <items_list> <args-for-the-list-widget>)
  59. ;; <items_list> is a list of BROWSER_OBJECTs as described above.
  60. ;; <args-for-the-list-widget> -- these are the arguments that
  61. ;;       will be passed on to the list widget
  62. ;;
  63. (send List_Browser :answer :isnew '(items_list &rest args)
  64.       '(
  65.     (let* (
  66.            (items_end_idx (length items_list))
  67.            (display_items (make-array items_end_idx)))
  68.  
  69.       ;; initialize the 'items' instance variable so that it
  70.       ;; holds all the BROWSER_OBJECTs passed in <items_list>
  71.       (setq items (make-array items_end_idx)) ;create the array
  72.       (do (                ;copy elts from list to array
  73.            (i    0          (1+ i))
  74.            (elts items_list (cdr elts)))
  75.           ;; loop till no more elts
  76.           ((null elts))
  77.           ;; loop body
  78.           (setf (aref items i) (car elts))
  79.           (setf (aref display_items i) (send (car elts) :display_string))
  80.           )
  81.  
  82.       ;; initialize the widget, passing in the browser items.
  83.       (apply 'send-super `(:isnew
  84.                    ,@args
  85.                    :xmn_selection_policy :browse_select
  86.                    :xmn_items ,display_items
  87.                    :xmn_item_count ,items_end_idx
  88.                    ))
  89.       )
  90.  
  91.     ;; set up a callback on the list widget initialized above such that
  92.     ;; a double click on the browser-item will send the message
  93.     ;; :default_action to the BROWSER_OBJECT.
  94.     (send-super :set_callback :xmn_default_action_callback
  95.             '(callback_item_position)
  96.             '((send (aref items (1- callback_item_position)) :default_action))
  97.             )
  98.     )
  99.       )
  100.  
  101. ;;
  102. ;; override methods on XM_LIST_WIDGET_CLASS so that they work properly
  103. ;; with the list browser. Note that all other list methods work fine
  104. ;; on the list browser
  105. ;;
  106. (send List_Browser :answer :ADD_ITEM '(item position)
  107.       '(
  108.     (setq items (array-insert-pos items (1- position) item))
  109.     (send-super :add_item (send item :display_string) position)
  110.     )
  111.       )
  112.  
  113. (send List_Browser :answer :ADD_ITEM_UNSELECTED '(item position)
  114.       '(
  115.     (setq items (array-insert-pos items (1- position) item))
  116.     (send-super :add_item_unselected (send item :display_string) position)
  117.     )
  118.       )
  119.  
  120. (send List_Browser :answer :DELETE_ITEM '(item)
  121.       '(
  122.     ;; this is too lame to implement... requires that we compare
  123.     ;; item with the result of :display_string done on every element
  124.     ;; of ivar 'items'
  125.     (error "Message :DELETE_ITEM not supported in List_Browser")
  126.     )
  127.       )
  128.  
  129. (send List_Browser :answer :DELETE_POS '(position)
  130.       '(
  131.     (setq items (array-delete-pos items (1- position)))
  132.     (send-super :delete_pos position)
  133.     )
  134.       )
  135.  
  136.  
  137. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  138. ;; Define a BROWSER_OBJECT
  139. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  140. ;;
  141. ;; Each BROWSER_OBJECT holds the information summarizing one mail message.
  142. ;; the information is split up into individual fields because we may want
  143. ;; to be able to sort on one field, or search for mathes on one field.
  144. ;;
  145. (setq Mail_Message_Class
  146.       (send Class :new
  147.         '(folder num anno month date no-date size sender subject)
  148.         ))
  149.  
  150. ;; this string is passed to the mh 'scan' and 'inc' commands to determine
  151. ;; the formatting of the output of the message info summary. Each entry
  152. ;; here corresponds to an instance variable in Mail_Message_Class
  153. (setq FOLDER_SCAN_FORMAT 
  154.       (strcat
  155.        "%(msg)"                ;output the message number
  156.        "%<{replied}A%|"            ;IF msg answered output "A" ELSE
  157.        "%<{forwarded}F%|"        ;IF msg forwarded output "F" ELSE
  158.        "%<{resent}R%|"            ;IF msg redisted output "R" ELSE
  159.        "%<{printed}P%|"            ;IF msg printed output "P"
  160.        " %>%>%>%>"            ;ELSE output " "
  161.        "%02(mon{date})/%02(mday{date})"    ;output mon/date
  162.        "%<{date} %|*%>"            ;IF no date output "*" else " "
  163.        "%(size) "            ;output the message's size
  164.        "%<(mymbox{from})To:%14(friendly{to})%|"    ;IF my message, output "To: <recipient>"
  165.        "%17(friendly{from})%> "        ;ELSE output sender field
  166.        "%{subject}<<"            ;output subject followed by ">>"
  167.        "%{body}"            ;output beginning of body, limited by SCAN_OUTPUT_WIDTH
  168.        )
  169.       )
  170.  
  171. ;; this method will read a single line summary of a mail message as produced
  172. ;; by the mh 'scan' or 'inc' commands and sets the instance variables in the 
  173. ;; BROWSER_OBJECT to the individual fields of the message summary.
  174. (send Mail_Message_Class :answer :read-msg-info '(pipe fldr)
  175.       '(
  176.     (if (and
  177.          (setq folder fldr)
  178.          (setq num     (fscanf-fixnum pipe "%ld"))
  179.          (setq anno    (fscanf-string pipe "%c"))
  180.          (setq month   (fscanf-fixnum pipe "%2ld"))
  181.          (setq date    (fscanf-fixnum pipe "/%2ld"))
  182.          (setq no-date (fscanf-string pipe "%c"))
  183.          (setq size    (fscanf-fixnum pipe "%d%*c"))
  184.          (setq sender  (fscanf-string pipe "%17[\001-\177]%*c"))
  185.          (setq subject (fscanf-string pipe "%[^\n]\n"))
  186.          )
  187.         self            ;return self if succesful
  188.       NIL                ;return NIL if hit EOF
  189.       )
  190.     )
  191.       )
  192.  
  193. (send Mail_Message_Class :answer :display_string '()
  194.       '(
  195.     (format nil
  196.         "~A ~A ~A/~A~A ~A ~A ~A"
  197.         num anno month date no-date size sender subject)
  198.     ))
  199.  
  200. (send Mail_Message_Class :answer :default_action '()
  201.       '((find-file (format nil "~A/~A/~A" MAILPATH folder num))))
  202.  
  203.  
  204. ;;
  205. ;; i'm too lazy to add a getenv() interface to WINTERP... this'll do for now.
  206. ;;
  207. (setq MAILPATH
  208.       (let*
  209.       ((pipe (popen "/bin/echo $HOME" "r"))
  210.        (home (read-line pipe))
  211.        )
  212.     (pclose pipe)
  213.     (strcat home "/Mail"))        ;this is the default directory
  214.                     ;for MH... this assumes you haven't
  215.                     ;put the MH directory elsewhere
  216.                     ;via a ~/.mh_profile entry.
  217.       )
  218.  
  219. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  220.  
  221. ;;
  222. ;; This returns a list of Mail_Message_Class instances corresponding
  223. ;; to the mail messages scanned from <foldername> over range <msgs>.
  224. ;;
  225. (defun mh-scan (foldername msgs)
  226.   (do* 
  227.    ((fp (popen (strcat "scan "
  228.                "+" foldername
  229.                " " msgs
  230.                " -noclear -noheader -reverse -width 80"
  231.                " -format '" FOLDER_SCAN_FORMAT "'")
  232.            :direction :input))
  233.     (msg (send (send Mail_Message_Class :new) :read-msg-info fp foldername)
  234.      (send (send Mail_Message_Class :new) :read-msg-info fp foldername))
  235.     (result NIL)
  236.     )
  237.    ((null msg)                ;:read-msg-info returns NIL on EOF
  238.     (pclose fp)
  239.     (cdr result)            ;last msg was EOF, remove it
  240.     )
  241.    (setq result (cons msg result))
  242.    )
  243.   )
  244.  
  245. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  246.  
  247. (setq top_w
  248.       (send TOP_LEVEL_SHELL_WIDGET_CLASS :new 
  249. ;        :XMN_GEOMETRY "500x700+1+1"
  250.         :XMN_TITLE "Mail Browser"
  251.         :XMN_ICON_NAME "Mail Browser"
  252.         ))
  253.  
  254. (setq paned_w
  255.       (send XM_PANED_WINDOW_WIDGET_CLASS :new :managed top_w
  256.         ))
  257.  
  258. (setq objs-list (mh-scan "inbox" "last:30"))
  259.  
  260. (setq list_w
  261.       (send List_Browser :new objs-list :managed :scrolled "browser" paned_w
  262.         :xmn_visible_item_count 10
  263.         ))
  264.  
  265. (setq label_w
  266.       (send XM_LABEL_WIDGET_CLASS :new :managed "label" paned_w
  267.         :xmn_label_string "None"
  268.         ))
  269.  
  270. ;;
  271. ;; set constraint resources on label widget so that paned window
  272. ;; doesn't give it resize sashes.
  273. ;;
  274. (let (height)
  275.   (send label_w :get_values :xmn_height 'height)
  276.   ;; In the code below, the kludgery
  277.   ;; "(if (and (eq *MOTIF_VERSION* 1) (eq *MOTIF_REVISION* 0)) ...)"
  278.   ;; is there to work around a name change between Motif 1.0 and 1.1:
  279.   ;; :XMN_MAXIMUM --> :XMN_PANE_MAXIMUM and :XMN_MINIMUM -->:XMN_PANE_MINIMUM
  280.   (send label_w :set_values
  281.     (if (and (eq *MOTIF_VERSION* 1) (eq *MOTIF_REVISION* 0))
  282.         :XMN_MAXIMUM :XMN_PANE_MAXIMUM)
  283.     height
  284.     (if (and (eq *MOTIF_VERSION* 1) (eq *MOTIF_REVISION* 0))
  285.         :XMN_MINIMUM :XMN_PANE_MINIMUM)
  286.     height
  287.     ))
  288.  
  289. (setq textedit_w 
  290.       (send XM_TEXT_WIDGET_CLASS :new :managed :scrolled "view" paned_w
  291.         :XMN_EDIT_MODE :MULTI_LINE_EDIT
  292.         :XMN_HEIGHT 200
  293.         :XMN_EDITABLE nil        ;don't allow user to change text.
  294.         ))
  295.  
  296. (send top_w :realize)
  297.  
  298. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  299. (defun find-file (file)
  300.   (let*
  301.       (;; loc vars
  302.        (fp
  303.     (open file :direction :input)
  304.     )
  305.        inspos
  306.        text_line
  307.        )
  308.  
  309.     (if (null fp)
  310.     (error "Can't open file." file))
  311.  
  312.     (send label_w :set_values
  313.       :xmn_label_string file)
  314.     (send textedit_w :set_string "")    ;clear out old text
  315.     (send paned_w :update_display)    ;incase reading file takes long time
  316.  
  317.     (send textedit_w :disable_redisplay NIL) ;don't show changes till done
  318.     (send textedit_w :replace 0 0 (read-line fp))
  319.     (loop
  320.      (if (null (setq text_line (read-line fp)))
  321.      (return))
  322.      (setq inspos (send textedit_w :get_insertion_position))
  323.      (send textedit_w :replace inspos inspos (strcat "\n" text_line))
  324.      )
  325.     (send textedit_w :enable_redisplay)    ;now show changes...
  326.     (close fp)
  327.     )
  328.   )
  329.